package spring.mybatis.domain;

import com.me.study.javaCore.enumm.BaseEnum;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

/**
 * TODO 自定义的类型处理器，用来处理 BaseEnum 类型的枚举类，有如下三种注册方式：
 *  1，参考 mybatis 的 SqlSessionFactoryBean.scanClasses(this.typeEnumsPackage, BaseEnum.class) 扫描 BaseEnum 类型的枚举类，
 *      然后手动注册 typeHandlerRegistry.register(clz, MybatisEnumTypeHandler.class);
 *  2，在 spring 中声明 SqlSessionFactory 时，使用 setTypeHandlersPackage 设置存放了 自定义TypeHandler 的包路径。
 *     同时，在自定义的 TypeHandler 通过注解指定自定义类型 @MappedTypes(GenderEnum.class)
 *  3，手动配置 mybatis 的 configuration，可以使用 xml 或者 javaConfig
 */
public class EnumValueHandler<E extends Enum<E> & BaseEnum> extends BaseTypeHandler<E> {
    private final Class<E> type;
    private final Map<Class<E>, Map<String, E>> CACHE = new HashMap<>();

    public EnumValueHandler(Class<E> type) {
        if (type == null) {
            throw new IllegalArgumentException("类型不能为 null");
        } else {
            if (!BaseEnum.class.isAssignableFrom(type)) {
                throw new RuntimeException("只能处理 BaseEnum 类型的枚举");
            }

            this.type = type;
            Map<String, E> enumMap = CACHE.computeIfAbsent(type, k -> new HashMap<>());
            E[] enumConstants = type.getEnumConstants();
            for (E e : enumConstants) {
                enumMap.put(e.getCode(), e);
            }

        }
    }

    public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
        if (jdbcType == null) {
            ps.setString(i, parameter.getCode());
        } else {
            ps.setObject(i, parameter.getCode(), jdbcType.TYPE_CODE);
        }

    }

    public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
        String val = rs.getString(columnName);
        return getEnum(val);
    }

    public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        String val = rs.getString(columnIndex);
        return getEnum(val);
    }

    public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        String val = cs.getString(columnIndex);
        return getEnum(val);
    }

    private E getEnum(String code) {
        return code == null ? null : CACHE.get(type).get(code);
    }
}
